#include "StdAfx.h" 
#include "Main_Wnd.h"
#include "Protocol.h"
#include "Protocol_CT.h"
#include "Hud.h"

#ifdef _DEBUG
#	pragma comment(lib, "opencv_highgui247d")
#	pragma comment(lib, "opencv_core247d")
#	pragma comment(lib, "opencv_imgproc247d")
#else
#	pragma comment(lib, "opencv_highgui247")
#	pragma comment(lib, "opencv_core247")
#	pragma comment(lib, "opencv_imgproc247")
#endif

class Capture_Widget : public QWidget
{
public:
	Capture_Widget(cv::Mat& frame, bool& is_ready, Main_Wnd* parent)
		: QWidget(parent)
		, m_main_wnd(parent)
		, m_frame(frame)
		, m_is_frame_ready(is_ready)
		, m_target_rect(0, 0, width(), height())
	{
 		//setAttribute(Qt::WA_OpaquePaintEvent);
 		//setAttribute(Qt::WA_NoSystemBackground);
		//m_painter.setRenderHint(QPainter::Antialiasing);

	}
	void paintEvent(QPaintEvent* ev)
	{
 		m_painter.begin(this);
 		m_painter.setCompositionMode(QPainter::CompositionMode_Source);

		//painter.setRenderHint(QPainter::Antialiasing);
		//painter.setRenderHint(QPainter::HighQualityAntialiasing);

		if (m_is_frame_ready)
		{
			if (m_img.width() != m_frame.cols)
			{
				m_img = QImage((const uchar*)m_frame.data, m_frame.cols, m_frame.rows, m_frame.step, QImage::Format_ARGB32_Premultiplied);

				float padding_left = (width() - m_img.width()) / 2.f;
				m_target_rect = QRect(padding_left, 0, width() - padding_left*2, height());
			}

			m_painter.drawImage(m_target_rect.topLeft(), m_img);
		}

		m_hud.paint(m_painter, QRectF(m_target_rect), ((Main_Wnd*)parent())->get_protocol().get_data());

		//messages
		{
			m_painter.setCompositionMode(QPainter::CompositionMode_Source);
			m_painter.setPen(QPen(Qt::white));
			float x = m_target_rect.x() + m_target_rect.width() - 2;
			float y = m_target_rect.height() - 4;
			{
				auto dm = m_main_wnd->get_deinterlace_method();
				if (dm != Deinterlacer::Method::NONE)
				{
					auto dm_name = (dm == Deinterlacer::Method::BOB) ? "bob" : "ela";
					m_hud.draw_text(m_painter, QPointF(x, y), dm_name, QTextOption(Qt::AlignRight));
					y -= 12;
				}
			}
			{
				auto sr = m_main_wnd->get_smooth_resize();
				if (sr)
				{
					m_hud.draw_text(m_painter, QPointF(x, y), "smooth", QTextOption(Qt::AlignRight));
					y -= 12;
				}
			}
		}

		m_painter.end();
		m_is_frame_ready = false;
	}

	Main_Wnd* m_main_wnd;
	Hud m_hud;
	cv::Mat& m_frame;
	bool& m_is_frame_ready;
	QPainter m_painter;
	QRect m_target_rect;
	QImage m_img;
};

//////////////////////////////////////////////////////////////////////////

Main_Wnd::Main_Wnd(QWidget *parent)
	: QMainWindow(parent)
	, m_is_frame_ready(false)
	, m_has_capture_device(false)
	, m_deinterlace_method(Deinterlacer::Method::NONE)
	, m_smooth_resize(false)
{
	m_ui.setupUi(this);

	setCentralWidget(new Capture_Widget(m_frame_resized, m_is_frame_ready, this));

	QTimer* timer = new QTimer(this);
	timer->setInterval(33);
	timer->setSingleShot(false);
	timer->start();
	connect(timer, &QTimer::timeout, this, &Main_Wnd::update);

	{
		QShortcut* shortcut = new QShortcut(QKeySequence("F11"), this, 0, 0, Qt::ApplicationShortcut);
		QObject::connect(shortcut, &QShortcut::activated, this, &Main_Wnd::toggle_fullscreen);

		shortcut = new QShortcut(QKeySequence("F5"), this, 0, 0, Qt::ApplicationShortcut);
		QObject::connect(shortcut, &QShortcut::activated, this, &Main_Wnd::toggle_capture);

		shortcut = new QShortcut(QKeySequence("D"), this, 0, 0, Qt::ApplicationShortcut);
		QObject::connect(shortcut, &QShortcut::activated, [this]() 
		{ 
			switch (m_deinterlace_method)
			{
			case Deinterlacer::Method::NONE: m_deinterlace_method = Deinterlacer::Method::BOB; break;
			case Deinterlacer::Method::BOB: m_deinterlace_method  = Deinterlacer::Method::ELA; break;
			case Deinterlacer::Method::ELA: m_deinterlace_method  = Deinterlacer::Method::NONE; break;
			}
		});

		shortcut = new QShortcut(QKeySequence("R"), this, 0, 0, Qt::ApplicationShortcut);
		QObject::connect(shortcut, &QShortcut::activated, [this]() { m_smooth_resize = !m_smooth_resize; });

		shortcut = new QShortcut(QKeySequence("Ctrl+0"), this, 0, 0, Qt::ApplicationShortcut);
		QObject::connect(shortcut, &QShortcut::activated, [this]() { set_camera(0); });
		shortcut = new QShortcut(QKeySequence("Ctrl+1"), this, 0, 0, Qt::ApplicationShortcut);
		QObject::connect(shortcut, &QShortcut::activated, [this]() { set_camera(1); });
	}

	m_protocol.reset(new Protocol_CT());

	try
	{
		start();
	}
	catch(std::exception e)
	{
		std::cout << e.what() << std::endl;
	}
}

Main_Wnd::~Main_Wnd()
{
}

void Main_Wnd::start()
{
	auto arg = QCoreApplication::arguments();
	if (arg.count() >= 3) //path, com, camera
	{
		m_capture.open(arg.at(2).toInt());//open first the camera to get something on screen even if the port fails
		m_has_capture_device = true;
		
		m_protocol->init(arg.at(1).toLatin1().data());
	}
	else
	{
		throw std::exception("Usage: fpv-monitor comX cameraIndex.\n\tcomX - X is the com port\n\tcameraIndex - 0 - internal webcam, 1 - the capture card usually");
	}
}

void Main_Wnd::update()
{
	if (!m_has_capture_device)
	{
		return;
	}

	m_protocol->update();

	if (!m_capture.isOpened())
	{
		return;
	}

// 	static int x = 0;
// 	if (x++ %8 == 0)
// 	{
		bool frame_grabbed = m_capture.retrieve(m_frame_bgr);
		m_capture.grab();

		if (frame_grabbed)
		{
			cv::cvtColor(m_frame_bgr, m_frame, CV_BGR2BGRA);

			cv::Mat deinterlaced_frame = m_deinterlacer.deinterlace(m_deinterlace_method, m_frame);

			//write to disk
			if (m_writer.isOpened())
			{
				m_writer << deinterlaced_frame;
			}

			cv::Size size(centralWidget()->size().width(), centralWidget()->size().height());
			{
				float image_ar = (float)m_frame_bgr.size().width / (float)m_frame_bgr.size().height;
				float ratio = (float)size.height / m_frame_bgr.size().height;
				float width = (float)size.height * image_ar;							    
				size.width = width;
			}
			cv::resize(deinterlaced_frame, m_frame_resized, size, 0, 0, m_smooth_resize ? cv::INTER_LINEAR : cv::INTER_NEAREST);
			m_is_frame_ready = true;
		}
// 	}

	((Capture_Widget*)centralWidget())->repaint();
}

const Protocol& Main_Wnd::get_protocol() const
{
	return *m_protocol;
}

void Main_Wnd::toggle_capture()
{
	if (m_writer.isOpened())
	{
		m_writer.release();
	}
	else
	{
		char mbstr[100];
		std::time_t t = std::time(NULL);
		if (std::strftime(mbstr, 100, "capture_%d-%m-%Y_%H-%M-%S.avi", std::localtime(&t))) 
		{
			cv::Size size = cv::Size((int)m_capture.get(CV_CAP_PROP_FRAME_WIDTH), (int)m_capture.get(CV_CAP_PROP_FRAME_HEIGHT));
			double fps = m_capture.get(CV_CAP_PROP_FPS);
			fps = fps == 0 ? 20 : fps;
			m_writer.open(mbstr, -1, fps, size, true);
			if (!m_writer.isOpened())
			{
				std::cout << "Cannot open video file" << std::endl;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////

void Main_Wnd::toggle_fullscreen()
{
	if (isFullScreen()) 
	{
		setWindowState(Qt::WindowMaximized);
	} 
	else 
	{
		setWindowState(Qt::WindowFullScreen);
	}
}

//////////////////////////////////////////////////////////////////////////

Deinterlacer::Method Main_Wnd::get_deinterlace_method() const
{
	return m_deinterlace_method;
}

bool Main_Wnd::get_smooth_resize() const
{
	return m_smooth_resize;
}

//////////////////////////////////////////////////////////////////////////

void Main_Wnd::set_camera(uint32_t idx)
{
	m_capture.open(idx);
	m_has_capture_device = true;
}